const http = require('http')
const path = require('path')
const fse = require('fs-extra')
const multiparty = require('multiparty')

const server = http.createServer()
// 大文件存储路径
const UPLOAD_DIR = path.resolve(__dirname, './dist')
// 提取文件后缀名
const extraExt = (filename) =>
	filename.slice(filename.lastIndexOf('.'), filename.length)

// 返回已上传的所有切片名
const crateUploadList = async (fileHash) =>
	fse.existsSync(path.resolve(UPLOAD_DIR, fileHash))
		? await fse.readdir(path.resolve(UPLOAD_DIR, fileHash))
		: []

server.on('request', async (req, res) => {
	res.setHeader('Access-Control-Allow-Origin', '*')
	res.setHeader('Access-Control-Allow-Headers', '*')

	if (req.method === 'OPTIONS') {
		res.status = 200
		return res.end()
	}

	if (req.url === '/upload') {
		const multipart = new multiparty.Form()
		multipart.parse(req, async (err, fields, files) => {
			if (err) return

			const [chunk] = files.chunk
			const [hash] = fields.hash
			const [filename] = fields.filename

			// 创建临时文件夹用于临时存储 chunk
			// 添加 chunkDir 前缀与文件名做区分
			const chunkDir = path.resolve(UPLOAD_DIR, 'chunkDir_' + filename)

			if (!fse.existsSync(chunkDir)) {
				await fse.mkdirs(chunkDir)
			} else {
				// return res.end(filename + ' already exists in server')
			}

			await fse.move(chunk.path, `${chunkDir}/${hash}`)
			return res.end('received file chunk')
		})
	}

	if (req.url === '/merge') {
		const data = await receivePostData(req)
		const { filename, size } = data
		const filePath = path.resolve(UPLOAD_DIR, filename)

		await mergeFileChunk(filePath, filename, size)
		return res.end(
			JSON.stringify({
				code: 0,
				message: 'file merge success'
			})
		)
	}

	if (req.url === '/verify') {
		const { filename, fileHash } = await receivePostData(req)
		const ext = extraExt(filename)
		const filePath = path.resolve(UPLOAD_DIR, `${fileHash}${ext}`)
		if (fse.existsSync(filePath)) {
			res.end(
				JSON.stringify({
					shouldUpload: false
				})
			)
		} else {
			res.end(
				JSON.stringify({
					shouldUpload: true,
					uploadedList: await crateUploadList(fileHash)
				})
			)
		}
	}
})

server.listen(9001, () => {
	console.log('listening port at 9001')
})

// 接收数据流
function receivePostData(req) {
	return new Promise((resolve, reject) => {
		let data = ''
		req.on('data', (chunk) => {
			data += chunk
		})

		req.on('end', () => {
			resolve(JSON.parse(data))
		})
	})
}

// 通过流的形式传输数据
function pipeStream(path, writeStream) {
	return new Promise((resolve, reject) => {
		const readStream = fse.createReadStream(path)
		readStream.pipe(writeStream)

		readStream.on('end', () => {
			fse.unlinkSync(path)
			resolve()
		})
	})
}

async function mergeFileChunk(filePath, filename, size) {
	const chunkDir = path.resolve(UPLOAD_DIR, 'chunkDir_' + filename)
	const chunkPaths = await fse.readdir(chunkDir)

	// 依据文件下标做排序
	chunkPaths.sort((a, b) => a.split('-')[1] - b.split('-')[1])

	await Promise.all(
		chunkPaths.map((chunkPath, index) =>
			pipeStream(
				path.resolve(chunkDir, chunkPath),
				fse.createWriteStream(filePath, { start: index * size })
			)
		)
	)
	fse.rmdirSync(chunkDir)
}
